#!/usr/bin/python2.5
# Copyright (C) 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
 Editor of GTFS feed.

 This is an extension of schedule_viewer.py, which allows editing
 of stop location usig drag'n'drop.
 You must provide a Google Maps API key.

 Usage:
 schedule_editor.py --key `cat key` --port 8765 --feed_filename feed.zip
"""

import cgi
import re
import urllib
import urlparse

from schedule_viewer import *
from transitfeed_editor import *
import gtfsscheduleeditor
from gtfsscheduleeditor.graph import PatternGraph

def MimeOpen(filename):
  """Try to open filename in the static files directory of this server.
  Return a tuple (file object, string mime_type) or raise an exception."""
  (mime_type, encoding) = mimetypes.guess_type(filename)
  assert mime_type
  # A crude guess of when we should use binary mode. Without it non-unix
  # platforms may corrupt binary files.
  if mime_type.startswith('text/'):
    mode = 'r'
  else:
    mode = 'rb'
  return open(filename, mode), mime_type

class ScheduleEditorRequestHandler(ScheduleRequestHandler):
  """Schedule with editing function on schedule feeds
  """

  def do_POST(self):
    """Post request dispatcher"""
    scheme, host, path, x, params, fragment = urlparse.urlparse(self.path)

    form = cgi.FieldStorage(keep_blank_values=1,
                            fp=self.rfile,
                            headers=self.headers,
                            environ={'REQUEST_METHOD':'POST',
                            'CONTENT_TYPE':self.headers['Content-Type'],
                            })
            
    parsed_params = {}
    # Variable parsed_params is a dict object processed by the following
    # actions to have it encoded in unicode and good enough for the handlers
    # to be processed smoothly
    for k in params.split('&'):
      k = urllib.unquote(k)
      if '=' in k:
        k, v = k.split('=', 1)
        parsed_params[k] = unicode(v, 'utf8')
      else:
        parsed_params[k] = ''

    if path == '/':
      return self.handle_GET_home()
    
    if path in ['/admin', '/admin/']:
      return self.handle_GET_admin()

    m = re.match(r'/admin/([a-z]{1,64})', path)
    if m:
      handler_name = 'handle_POST_admin_%s' % m.group(1)
      handler = getattr(self, handler_name, None)
      if callable(handler):
        return handler(parsed_params, form)

    m = re.match(r'/post/([a-z]{1,64})', path)

    # Variable path must match the given regex before being dispatched,
    # because the handler is related to the path name.
    if m:
      handler_name = 'handle_POST_%s' % m.group(1)
      handler = getattr(self, handler_name, None)
      if callable(handler):

        return self.handle_json_wrapper_POST(handler, parsed_params, form)
      
  def handle_POST_admin_agency(self, GET, POST):
    db = self.server.schedule._db
    act = GET.get('act', None)
    id = GET.get('id', None)
    params = {}
    fields = {}
    for key in Agency._FIELD_NAMES:
      fields[key] = POST[key].value

    if act == 'edit' and id:
      print 'agency edit'
      GTFSBase.UpdateItem(Agency, db, fields, id)
      params['prompt'] = "Agency %s Updated!" % id
    else:
      GTFSBase.InsertItem(Agency, db, fields)
      params['prompt'] = "Agency %s Inserted!" % fields['agency_id']
 
    params['url'] = "/admin/agency"
      
    self.handle_GET_admin_agency(params)

  def handle_POST_admin_route(self, GET, POST):
    db = self.server.schedule._db
    act = GET.get('act', None)
    id = GET.get('id', None)
    agency_id = GET.get('agency_id', None)
    params = {}
    fields = {}
    for key in Route._FIELD_NAMES:
      fields[key] = POST[key].value

    if act == 'edit' and id:
      print 'agency edit'
      GTFSBase.UpdateItem(Route, db, fields, id)
      params['prompt'] = "Route %s Updated!" % id
    else:
      GTFSBase.InsertItem(Route, db, fields)
      params['prompt'] = "Route %s Inserted!" % fields['route_id']
 
    params['url'] = "/admin/route"
      
    self.handle_GET_admin_route(params)

  def handle_POST_admin_stoptime(self, GET, POST):
    db = self.server.schedule._db
    act = GET.get('act', None)
    trip_id = GET.get('id', None)
    sequence = GET.get('sequence', None)
    params = {}
    fields = {}
    for key in StopTime._FIELD_NAMES:
      fields[key] = POST[key].value

    if act == 'edit' and trip_id and sequence:
      filter = '`trip_id` = "%s" and `stop_sequence` = "%s"' % (trip_id, sequence)
      GTFSBase.UpdateItemBy(StopTime, db, fields, filter)
      params['prompt'] = "StopTime %s Updated!" % id
    else:
      GTFSBase.InsertItem(StopTime, db, fields)
      params['prompt'] = "StopTime Inserted!"

    params['url'] = "/admin/stoptime"
    self.handle_GET_admin_stoptime(params)


  def handle_POST_admin_trip(self, GET, POST):
    db = self.server.schedule._db
    act = GET.get('act', None)
    id = GET.get('id', None)
    params = {}
    fields = {}
    for key in Trip._FIELD_NAMES:
      fields[key] = POST[key].value

    if act == 'edit' and id:
      filter = '`trip_id` = "%s"' % (id)
      GTFSBase.UpdateItemBy(Trip, db, fields, filter)
      params['prompt'] = "Trip %s Updated!" % id
    else:
      GTFSBase.InsertItem(Trip, db, fields)
      params['prompt'] = "Trip Inserted!"

    params['url'] = "/admin/trip"
    self.handle_GET_admin_trip(params)

  def handle_POST_admin_shape(self, GET, POST):
    db = self.server.schedule._db
    act = GET.get('act', None)
    id = GET.get('id', None)
    params = {}
    shape_id = POST['shape_id'].value

    if act == 'edit' and id:
      #filter = '`shape_id` = "%s"' % (id)
      Shape.UpdateID(db, id, shape_id)
      #Trip.UpdateShapeID(shape_id, id)
      #Trip.UpdateShapeID(shape_id, id)
      #GTFSBase.UpdateItemBy(Shape, db, fields, filter)
      params['prompt'] = "Shape %s Updated!" % id
    else:
      Shape.Insert(shape_id)
      #GTFSBase.InsertItem(Shape, db, fields)
      params['prompt'] = "Shape Inserted!"

    params['url'] = "/admin/shape"
    self.handle_GET_admin_shape(params)

  def handle_POST_admin_transfer(self, GET, POST):
    db = self.server.schedule._db
    act = GET.get('act', None)
    fid = GET.get('fid', None)
    tid = GET.get('tid', None)
    params = {}
    fields = {}
    for key in Transfer._FIELD_NAMES:
      fields[key] = POST[key].value

    if act == 'edit' and fid and tid:
      filter = '`from_stop_id` = "%s" and `to_stop_id` = "%s"' % (fid, tid)
      GTFSBase.UpdateItemBy(Transfer, db, fields, filter)
      params['prompt'] = "Transfer %s Updated!" % id
    else:
      GTFSBase.InsertItem(Transfer, db, fields)
      params['prompt'] = "Transfer Inserted!"

    params['url'] = "/admin/transfer"
    self.handle_GET_admin_transfer(params)


  def handle_POST_admin_fare(self, GET, POST):
    db = self.server.schedule._db
    act = GET.get('act', None)
    id = GET.get('id', None)
    date = GET.get('date', None)
    params = {}
    fields = {}
    for key in Fare._FIELD_NAMES:
      fields[key] = POST[key].value

    if act == 'edit' and id:
      filter = '`fare_id` = "%s"' % (id)
      GTFSBase.UpdateItemBy(Fare, db, fields, filter)
      params['prompt'] = "Fare %s Updated!" % id
    else:
      GTFSBase.InsertItem(Fare, db, fields)
      params['prompt'] = "Fare %s Inserted!" % (fields['fare_id'])

    params['url'] = "/admin/fare"
    self.handle_GET_admin_fare(params)

  def handle_POST_admin_farerule(self, GET, POST):
    db = self.server.schedule._db
    act = GET.get('act', None)
    id = GET.get('id', None)
    route_id = GET.get('route_id', None)
    date = GET.get('date', None)
    params = {}
    fields = {}
    for key in FareRule._FIELD_NAMES:
      fields[key] = POST[key].value

    if act == 'edit' and id:
      filter = '`fare_id` = "%s" and `route_id` = "%s"' % (id, route_id)
      GTFSBase.UpdateItemBy(FareRule, db, fields, filter)
      params['prompt'] = "FareRule %s Updated!" % id
    else:
      GTFSBase.InsertItem(FareRule, db, fields)
      params['prompt'] = "FareRule %s Inserted!" % (fields['fare_id'])

    params['url'] = "/admin/farerule"
    self.handle_GET_admin_farerule(params)

  def handle_POST_admin_frequency(self, GET, POST):
    db = self.server.schedule._db
    act = GET.get('act', None)
    id = GET.get('id', None)
    date = GET.get('date', None)
    params = {}
    fields = {}
    for key in Frequency._FIELD_NAMES:
      fields[key] = POST[key].value

    if act == 'edit' and id:
      filter = '`trip_id` = "%s"' % (id)
      GTFSBase.UpdateItemBy(Frequency, db, fields, filter)
      params['prompt'] = "Frequency %s Updated!" % id
    else:
      GTFSBase.InsertItem(Frequency, db, fields)
      params['prompt'] = "Frequency %s Inserted!" % (fields['trip_id'])

    params['url'] = "/admin/frequency"
    self.handle_GET_admin_frequency(params)



  def handle_POST_admin_calendardate(self, GET, POST):
    db = self.server.schedule._db
    act = GET.get('act', None)
    id = GET.get('id', None)
    date = GET.get('date', None)
    params = {}
    fields = {}
    for key in CalendarDate._FIELD_NAMES:
      fields[key] = POST[key].value

    if act == 'edit' and id and date:
      filter = '`service_id` = "%s" and `date` = "%s"' % (id, date)
      GTFSBase.UpdateItemBy(CalendarDate, db, fields, filter)
      params['prompt'] = "CalendarDate %s Updated!" % id
    else:
      GTFSBase.InsertItem(CalendarDate, db, fields)
      params['prompt'] = "CalendarDate (%s, %s) Inserted!" % (fields['service_id'], fields['date'])

    params['url'] = "/admin/calendardate"
    self.handle_GET_admin_calendardate(params)

  def handle_POST_admin_calendar(self, GET, POST):
    db = self.server.schedule._db
    act = GET.get('act', None)
    id = GET.get('id', None)
    params = {}
    fields = {}
    for key in Calendar._FIELD_NAMES:
      fields[key] = POST[key].value

    if act == 'edit' and id:
      GTFSBase.UpdateItem(Calendar, db, fields, id)
      params['prompt'] = "Calendar %s Updated!" % id
    else:
      GTFSBase.InsertItem(Calendar, db, fields)
      params['prompt'] = "Calendar %s Inserted!" % fields['service_id']

    params['url'] = "/admin/calendar"
    self.handle_GET_admin_calendar(params)

  def handle_POST_admin_stop(self, GET, POST):
    db = self.server.schedule._db
    act = GET.get('act', None)
    id = GET.get('id', None)
    params = {}
    fields = {}
    for key in Stop._FIELD_NAMES:
      fields[key] = POST[key].value

    if act == 'edit' and id:
      GTFSBase.UpdateItem(Stop, db, fields, id)
      params['prompt'] = "Stop %s Updated!" % id
    else:
      GTFSBase.InsertItem(Stop, db, fields)
      params['prompt'] = "Stop %s Inserted!" % fields['stop_id']

    params['url'] = "/admin/stop"
    self.handle_GET_admin_stop(params)


  def handle_json_wrapper_POST(self, handler, parsed_params, form):
    """Call handler and output the return value in JSON."""
    schedule = self.server.schedule
    result = handler(parsed_params, form)
    content = ResultEncoder().encode(result)
    self.send_response(200)
    self.send_header('Content-Type', 'text/plain')
    self.send_header('Content-Length', str(len(content)))
    self.end_headers()
    self.wfile.write(content)

  def handle_POST_shape(self, params, form):
    """parse shape data and store it into the database"""
    db = self.server.schedule._db
    id = form['shapeId'].value
    points = form.getlist('points')
    return Shape.SavePoints(db, id, points)

  def handle_POST_pattern(self, params, form):
    """Handle pattern related info"""
    act = form['act'].value
    if act == 'updatestoptime':
      return self.updatePatternStop(form)
    if act == 'deletestoptime':
      return self.deletePatternStop(form)

  def deletePatternStop(self, form):
    """Delete pattern stop"""
    db = self.server.schedule._db
    routeId = form['rid'].value
    stopId = form['sid'].value
    sequence = form['seq'].value
    patternId = int(form['pid'].value)
    StopTime.DeleteByPattern(db, routeId, patternId, sequence, stopId)
    return 1

  def updatePatternStop(self, form):
    """Update pattern stop to new stop"""
    db = self.server.schedule._db
    routeId = form['rid'].value
    sequence = form['seq'].value
    arrivalTime = form['atime'].value
    departureTime = form['dtime'].value
    stopId = form['sid'].value
    patternId = int(form['pid'].value)
    isEnd = int(form['isend'].value)
    StopTime.InsertStopTimeIntoPattern(db, patternId, sequence,
        arrivalTime, departureTime, stopId, isEnd)
    return 1

  def handle_POST_stop(self, params, form):
    """Parse stop data and store it into the database"""
    act = form['act'].value
    if act == 'save':
      return self.saveStop(form)
    if act == 'copy':
      return self.copyStop(form)
    if act == 'add':
      return self.addStop(form)

  def addStop(self, form):
    """Add a stop to the shape"""
    db = self.server.schedule._db
    newId = Stop.GetInsertId(db)
    name = form['name'].value.decode('utf-8')
    (lat, lng) = form['latlng'].value.split(',')
    lat = float(lat)
    lon = float(lng)
    Stop.Insert(db, newId, name, lat, lon)
    return [{'id': newId}]

  def copyStop(self, form):
    db = self.server.schedule._db
    parentId = form['parentId'].value
    name = form['name'].value.decode('utf-8')
    (lat, lon) = form['latlng'].value.split(',')
    id = Stop.GetInsertId(db)
    parentStation = Stop.CopyStop(db, id, parentId, name, lat, lon)
    return [{'id': id, 'parentStation': parentStation}]

  def saveStop(self, form):
    db = self.server.schedule._db
    id = form['stopId'].value
    name = form['name'].value.decode('utf-8')
    (lat, lon) = form['latlng'].value.split(',')
    lat = float(lat)
    lon = float(lon)
    Stop.UpdateNameLatLng(db, id, name, lat, lon);
    return 1

  def AllowEditMode(self):
    """Allow Edit"""
    return True

  def do_GET(self):
    scheme, host, path, x, params, fragment = urlparse.urlparse(self.path)
    parsed_params = {}
    for k in params.split('&'):
      k = urllib.unquote(k)
      if '=' in k:
        k, v = k.split('=', 1)
        parsed_params[k] = unicode(v, 'utf8')
      else:
        parsed_params[k] = ''

    if path == '/':
      return self.handle_GET_home()

    if path in ['/admin', '/admin/']:
      return self.handle_GET_admin()

    m = re.match(r'/admin/([a-z]{1,64})', path)
    if m:
      handler_name = 'handle_GET_admin_%s' % m.group(1)
      handler = getattr(self, handler_name, None)
      if callable(handler):
        return handler(parsed_params)

    m = re.match(r'/js/(.*)', path)
    if m:
      return self.handle_static_file(m.group(1))

    m = re.match(r'/json/([a-z]{1,64})', path)
    if m:
      handler_name = 'handle_json_GET_%s' % m.group(1)
      handler = getattr(self, handler_name, None)
      if callable(handler):
        return self.handle_json_wrapper_GET(handler, parsed_params)

    # Restrict allowable file names to prevent relative path attacks etc
    m = re.match(r'/file/([a-z0-9_-]{1,64}\.?[a-z0-9_-]{1,64})$', path)
    if m and m.group(1):
      try:
        f, mime_type = self.OpenFile(m.group(1))
        return self.handle_static_file_GET(f, mime_type)
      except IOError, e:
        print "Error: unable to open %s" % m.group(1)
        # Ignore and treat as 404

    m = re.match(r'/([a-z]{1,64})', path)
    if m:
      handler_name = 'handle_GET_%s' % m.group(1)
      handler = getattr(self, handler_name, None)
      if callable(handler):
        return handler(parsed_params)

    return self.handle_GET_default(parsed_params, path)


  def render(self, file, params):
    template = templateEnv.get_template(file)
    content = template.render(params)
    self.send_response(200)
    self.send_header('Content-Type', 'text/html')
    self.send_header('Content-Length', str(len(content)))
    self.end_headers()
    self.wfile.write(content)

  def handle_GET_admin_stop(self, params):
    """Default request handler"""
    db = self.server.schedule._db
    submit = 'Add'
    js = '/js/stop.js'
    title = "Stop Editor"
    stop = None

    url = params.get('url', '')
    act = params.get('act', None)
    id = params.get('id', "")
    prompt = params.get('prompt', '')
    page = params.get('page', 0)
    limit = params.get('limit', 1000)
    filter = '1'
    if act == 'edit' and id:
      print "edit"
      stop = GTFSBase.GetItem(Stop, db, id)
      submit = "Modify"
    db = self.server.schedule._db
    stops = GTFSBase.GetPage(Stop, db, filter, page, limit)
    self.render('stop.html', {
                              "title": title,
                              "js": js,
                              "submit": submit,
                              "stop": stop,
                              "stops": stops,
                              "prompt": prompt,
                              "url": url})

  def handle_GET_admin_shape(self, params):
    """Default request handler"""
    db = self.server.schedule._db
    submit = 'Add'
    js = '/js/shape.js'
    title = "Shape Editor"

    url = params.get('url', '')
    act = params.get('act', None)
    id = params.get('id', "")
    prompt = params.get('prompt', '')
    page = params.get('page', 1)
    limit = params.get('limit', 1000)
    filter = '1'
    shape_id = ''
    if act == 'edit' and id:
      shape_id = id
      submit = "Modify"
    db = self.server.schedule._db
    shapes = Shape.GetPage(db, '1', page, limit)

    self.render('shape.html', {
                              "title": title,
                              "js": js,
                              "submit": submit,
                              "shape_id": shape_id,
                              "shapes": shapes,
                              "prompt": prompt,
                              "url": url})

  def handle_GET_admin_stoptime(self, params):
    """Default request handler"""
    db = self.server.schedule._db
    submit = 'Add'
    js = '/js/stoptime.js'
    title = "StopTime Editor"
    stoptime = None

    url = params.get('url', '')
    act = params.get('act', None)
    trip_id = params.get('id', "")
    sequence = params.get('sequence', "")
    prompt = params.get('prompt', '')
    page = params.get('page', 1)
    limit = params.get('limit', 1000)
    filter = '1'
    if act == 'edit' and trip_id and sequence:
      print "edit"
      filter = '`trip_id` = "%s" and `stop_sequence` = "%s"' % (trip_id, sequence)
      stoptime = GTFSBase.GetItemBy(StopTime, db, filter)
      print stoptime
      submit = "Modify"
    db = self.server.schedule._db
    stoptimes = GTFSBase.GetPage(StopTime, db, '1', page, limit)
    trips = GTFSBase.GetPage(Trip, db, '1', page, limit)
    services = GTFSBase.GetPage(Calendar, db, '1', page, limit)
    stops = GTFSBase.GetPage(Stop, db, '1', page, limit)

    self.render('stoptime.html', {
                              "title": title,
                              "js": js,
                              "submit": submit,
                              "stoptime": stoptime,
                              "stoptimes": stoptimes,
                              "trips": trips,
                              "stops": stops,
                              "prompt": prompt,
                              "url": url})

  def handle_GET_admin_trip(self, params):
    """Default request handler"""
    db = self.server.schedule._db
    submit = 'Add'
    js = '/js/trip.js'
    title = "Trip Editor"
    trip = None

    url = params.get('url', '')
    act = params.get('act', None)
    id = params.get('id', "")
    prompt = params.get('prompt', '')
    page = params.get('page', 1)
    limit = params.get('limit', 1000)
    filter = '1'
    if act == 'edit' and id:
      print "edit"
      filter = '`trip_id` = "%s"' % (id)
      trip = GTFSBase.GetItemBy(Trip, db, filter)
      submit = "Modify"
    db = self.server.schedule._db
    trips = GTFSBase.GetPage(Trip, db, '1', page, limit)
    routes = GTFSBase.GetPage(Route, db, '1', page, limit)
    services = GTFSBase.GetPage(Calendar, db, '1', page, limit)
    shapes = Shape.GetPage(db, '1', page, limit)

    self.render('trip.html', {
                              "title": title,
                              "js": js,
                              "submit": submit,
                              "trip": trip,
                              "trips": trips,
                              "routes": routes,
                              "services": services,
                              "shapes": shapes,
                              "prompt": prompt,
                              "url": url})



  def handle_GET_admin_transfer(self, params):
    """Default request handler"""
    db = self.server.schedule._db
    submit = 'Add'
    js = '/js/transfer.js'
    title = "Transfer Editor"
    transfer = None

    url = params.get('url', '')
    act = params.get('act', None)
    fid = params.get('fid', "")
    tid = params.get('tid', "")
    prompt = params.get('prompt', '')
    page = params.get('page', 0)
    limit = params.get('limit', 1000)
    filter = '1'
    if act == 'edit' and tid and fid:
      print "edit"
      filter = '`from_stop_id` = "%s" and `to_stop_id` = "%s"' % (fid, tid)
      transfer = GTFSBase.GetItemBy(Transfer, db, filter)
      submit = "Modify"
    db = self.server.schedule._db
    transfers = GTFSBase.GetPage(Transfer, db, '1', page, limit)
    stops = GTFSBase.GetPage(Stop, db, '1', page, limit)

    self.render('transfer.html', {
                              "title": title,
                              "js": js,
                              "submit": submit,
                              "transfer": transfer,
                              "transfers": transfers,
                              "stops": stops,
                              "prompt": prompt,
                              "url": url})

  def handle_GET_admin_fare(self, params):
    """Default request handler"""
    db = self.server.schedule._db
    submit = 'Add'
    js = '/js/fare.js'
    title = "Fare Editor"
    fare = None

    url = params.get('url', '')
    act = params.get('act', None)
    id = params.get('id', "")
    prompt = params.get('prompt', '')
    page = params.get('page', 0)
    limit = params.get('limit', 1000)
    filter = '1'
    if act == 'edit' and id:
      print "edit"
      filter = '`fare_id` = "%s"' % (id)
      fare = GTFSBase.GetItemBy(Fare, db, filter)
      submit = "Modify"
    db = self.server.schedule._db
    fares = GTFSBase.GetPage(Fare, db, '1', page, limit)
    routes = GTFSBase.GetAll(Route, db)

    self.render('fare.html', {
                              "title": title,
                              "js": js,
                              "submit": submit,
                              "fare": fare,
                              "fares": fares,
                              "prompt": prompt,
                              "url": url})

  def handle_GET_admin_farerule(self, params):
    """Default request handler"""
    db = self.server.schedule._db
    submit = 'Add'
    js = '/js/farerule.js'
    title = "FareRule Editor"
    farerule = None

    url = params.get('url', '')
    act = params.get('act', None)
    id = params.get('id', "")
    route_id = params.get('route_id', "")
    prompt = params.get('prompt', '')
    page = params.get('page', 0)
    limit = params.get('limit', 1000)
    filter = '1'
    if act == 'edit' and id and route_id:
      print "edit"
      filter = '`fare_id` = "%s" and `route_id` = "%s"' % (id, route_id)
      farerule = GTFSBase.GetItemBy(FareRule, db, filter)
      route_id = farerule['route_id']
      submit = "Modify"
    db = self.server.schedule._db
    farerules = GTFSBase.GetPage(FareRule, db, '1', page, limit)
    routes = GTFSBase.GetAll(Route, db)

    self.render('farerule.html', {
                              "title": title,
                              "js": js,
                              "submit": submit,
                              "farerule": farerule,
                              "route_id": route_id,
                              "routes": routes,
                              "farerules": farerules,
                              "prompt": prompt,
                              "url": url})


  def handle_GET_admin_frequency(self, params):
    """Default request handler"""
    db = self.server.schedule._db
    submit = 'Add'
    js = '/js/frequency.js'
    title = "Frequency Editor"
    frequency = None

    url = params.get('url', '')
    act = params.get('act', None)
    id = params.get('id', "")
    date = params.get('date', "")
    prompt = params.get('prompt', '')
    page = params.get('page', 0)
    limit = params.get('limit', 1000)
    filter = '1'
    if act == 'edit' and id:
      print "edit"
      filter = '`trip_id` = "%s"' % (id)
      frequency = GTFSBase.GetItemBy(Frequency, db, filter)
      submit = "Modify"
    db = self.server.schedule._db
    frequencies = GTFSBase.GetPage(Frequency, db, filter, page, limit)
    self.render('frequency.html', {
                              "title": title,
                              "js": js,
                              "submit": submit,
                              "frequency": frequency,
                              "frequencies": frequencies,
                              "prompt": prompt,
                              "url": url})


  def handle_GET_admin_calendardate(self, params):
    """Default request handler"""
    db = self.server.schedule._db
    submit = 'Add'
    js = '/js/calendardate.js'
    title = "CalendarDate Editor"
    calendardate = None

    url = params.get('url', '')
    act = params.get('act', None)
    id = params.get('id', "")
    date = params.get('date', "")
    prompt = params.get('prompt', '')
    page = params.get('page', 0)
    limit = params.get('limit', 1000)
    filter = '1'
    if act == 'edit' and id and date:
      print "edit"
      filter = '`service_id` = "%s" and `date` = "%s"' % (id, date)
      calendardate = GTFSBase.GetItemBy(CalendarDate, db, filter)
      submit = "Modify"
    db = self.server.schedule._db
    calendardates = GTFSBase.GetPage(CalendarDate, db, filter, page, limit)
    self.render('calendardate.html', {
                              "title": title,
                              "js": js,
                              "submit": submit,
                              "service": calendardate,
                              "services": calendardates,
                              "prompt": prompt,
                              "url": url})


  def handle_GET_admin_calendar(self, params):
    """Default request handler"""
    db = self.server.schedule._db
    submit = 'Add'
    js = '/js/calendar.js'
    title = "Calendar Editor"
    calendar = None

    url = params.get('url', '')
    act = params.get('act', None)
    id = params.get('id', "")
    prompt = params.get('prompt', '')
    page = params.get('page', 0)
    limit = params.get('limit', 1000)
    filter = '1'
    if act == 'edit' and id:
      print "edit"
      calendar = GTFSBase.GetItem(Calendar, db, id)
      submit = "Modify"
    db = self.server.schedule._db
    calendars = GTFSBase.GetPage(Calendar, db, filter, page, limit)
    self.render('calendar.html', {
                              "title": title,
                              "js": js,
                              "submit": submit,
                              "service": calendar,
                              "services": calendars,
                              "prompt": prompt,
                              "url": url})

  def handle_GET_admin_route(self, params):
    """Default request handler"""
    db = self.server.schedule._db
    submit = 'Add'
    js = '/js/route.js'
    title = "Route Editor"

    url = params.get('url', '')
    act = params.get('act', None)
    id = params.get('id', "")
    prompt = params.get('prompt', '')
    page = params.get('page', 0)
    limit = params.get('limit', 10)
    agency_id = params.get('agency_id', '')
    route_type = ''
    route = None
    filter = '1'
    if agency_id:
      filter = '`agency_id` = "%s"' % agency_id
    
    if act == 'edit' and id:
      print "edit"
      #route = Route.GetItem(db, id)
      route = GTFSBase.GetItem(Route, db, id)
      agency_id = route['agencyId']
      route_type = route['type']
      print route
      submit = "Modify"
    db = self.server.schedule._db
    agencies = GTFSBase.GetAll(Agency, db)
    routes = GTFSBase.GetPage(Route, db, filter, page, limit)

    self.render('route.html', {
                              "title": title,
                              "js": js,
                              "submit": submit,
                              "route": route,
                              "routes": routes,
                              "agency_id": agency_id,
                              "agencies": agencies,
                              "prompt": prompt,
                              "route_type": route_type,
                              "url": url})

  def handle_GET_admin_agency(self, params):
    """Default request handler"""
    db = self.server.schedule._db
    submit = 'Add'
    js = '/js/agency.js'
    title = "Agency Editor"
    page = 0
    limit = 10
    tz = ""
    lang = ''
    url = params.get('url', '')
    act = params.get('act', None)
    id = params.get('id', "")
    prompt = params.get('prompt', '')
    print prompt
    agency = None
    
    if act == 'edit' and id:
      print "edit"
      #agency = Agency.GetItem(db, id)
      agency = GTFSBase.GetItem(Agency, db, id)
      print agency
      tz = agency['timezone']
      lang = agency['lang']
      submit = "Modify"

    db = self.server.schedule._db
    #agencies = Agency.GetPage(db, page, limit)
    agencies = GTFSBase.GetPage(Agency, db, '1', page, limit)

    self.render('agency.html', { 'title': title,
                                'js': js,
                                'submit': submit,
                                'agency': agency,
                                'agencies': agencies,
                                'prompt': prompt,
                                'tz': tz,
                                'lang': lang,
                                'url': url})

  def handle_static_file(self, filename):
    f, mime_type = MimeOpen("res/js/" + filename)
    #(mime_type, encoding) = mimetypes.guess_type(filename)
    return self.handle_static_file_GET(f, mime_type)

  def handle_GET_home(self):
    """Default request handler"""
    db = self.server.schedule._db

    # Get the smallest rectangle which containing all the stops in the feed
    # as the defaut area of the map. It is used to determine the zoom level
    # and the center of the map
    (min_lat, min_lon, max_lat, max_lon) = Stop.GetBoundingBox(db)
    forbid_editing = ('true', 'false')[self.AllowEditMode()]

    agency = ""
    key = self.server.key
    host = self.server.host

    # A very simple template system. For a fixed set of values replace [xxx]
    # with the value of local variable xxx
    f, _ = self.OpenFile('index.html')
    content = f.read()
    for v in ('agency', 'min_lat', 'min_lon', 'max_lat', 'max_lon', 'key',
              'host', 'forbid_editing'):
      content = content.replace('[%s]' % v, str(locals()[v]))

    self.send_response(200)
    self.send_header('Content-Type', 'text/html')
    self.send_header('Content-Length', str(len(content)))
    self.end_headers()
    self.wfile.write(content)

  def handle_GET_admin(self, params = {}):
    """Default request handler"""

    content = "Hello Admin!"

    self.send_response(200)
    self.send_header('Content-Type', 'text/html')
    self.send_header('Content-Length', str(len(content)))
    self.end_headers()
    self.wfile.write(content)


  def handle_GET_ttablegraph(self,params):
    """Draw a Marey graph in SVG for a pattern (collection of trips in a route
    that visit the same sequence of stops)."""
    db = self.server.schedule._db
    graph = PatternGraph()

    trip_id = params.get('trip', None)
    height = int(params.get('height', 300))

    pattern_id = PatternTrip.getPatternIdByTripId(db, trip_id)
    (start, end) = PatternTrip.getPatternMinMaxTime(db, pattern_id)
    graph.SetSpan(TimeStringToSeconds(start), TimeStringToSeconds(end))
    gaps = Trip.GetStopGapsByTripId(db, trip_id)
    content = graph.getDraw(db, pattern_id, gaps, height)

    self.send_response(200)
    self.send_header('Content-Type', 'image/svg+xml')
    self.send_header('Content-Length', str(len(content)))
    self.end_headers()
    self.wfile.write(content)

  def handle_json_GET_agency(self, params):
    """Return a list of all agencies."""
    db = self.server.schedule._db;
    return GTFSBase.GetAll(Agency, db)
    #return Agency.GetAll(db)

  def handle_json_GET_routesbyaid(self, params):
    """Return a list of routes by agency, aid == "0" for all routes"""
    db = self.server.schedule._db;
    aid = params.get('aid', None)
    routes = []
    if aid is None:
      return routes
    if aid == "0":
      routesList  = db.GetAll(Route._TABLE_NAME)
    else:
      routesList = db.GetAll(Route._TABLE_NAME,
        "`agency_id` = '%s'" % (aid))
    for route in routesList:
      routes.append({
                    'id': route[0],
                    'shortName': route[1],
                    'longName': route[2],
                    'type': route[3],
                    'agencyId': route[4],
                    'desc': route[5],
                    'url': route[6],
                    'color': route[7],
                    'textColor': route[8]
                    })
    def sort_routes(r1, r2):
      rn1 = r1['longName'] or r1['shortName']
      rn2 = r2['longName'] or r2['shortName']
      return cmp(rn1, rn2)
    routes.sort(sort_routes)
    return routes

  def handle_json_GET_gtfsexport(self, params):
    """Export GTFS Data to a new zip file"""
    schedule = self.server.schedule
    schedule.SaveToFeedFile()
    return [1]

  def handle_json_GET_tripsbyrid(self, params):
    """Return trips of a Route object"""
    db = self.server.schedule._db;
    id = params.get('rid', None)

    trips = Trip.GetPatternTripsByRouteId(db, id)
    
    ret = {}
    for trip in trips:
      ret.setdefault(trip[0], []).append({
                 'id': trip[1],
                 'headsign': trip[2],
                 'serviceId': trip[3],
                 'direction': trip[4],
                 'blockId': trip[5],
                 'shapeId': trip[6],
                 'shapeDesc': trip[7],
                 'startTime': trip[8]
                 })
    return [ret]

  def handle_json_GET_stoptimesbytid(self, params):
    """Return stoptimes of a Trip object"""
    db = self.server.schedule._db
    tid = params.get('tid')
    stoptimes = StopTime.GetStopTimesByTripId(db, tid)
    ret = []
    for stoptime in stoptimes:
      ret.append({'arrivalTime': stoptime[0],
                  'departureTime': stoptime[1],
                  'sequence': stoptime[2],
                  'stop': {
                           'id': stoptime[3],
                           'name': stoptime[4],
                           'lat': stoptime[5],
                           'lng': stoptime[6],
                           'parentStation': stoptime[7],
                           'locationType': stoptime[8]
                          }
                 })
    return ret

  def handle_json_GET_shapeexist(self, params):
    """Judge the existence of the given shape"""
    db = self.server.schedule._db
    id = params.get('id')
    return [Shape.Exist(db, id)]
  
  def handle_json_GET_shapesbytid(self, params):
    """return shape points for the given trip id"""
    db = self.server.schedule._db
    tid = params.get('tid')
    shape_points = Trip.GetShapePointsByTripId(db, tid)

    points = []
    for point in shape_points:
      points.append({'lat': point[0], 'lng': point[1], "dist": point[2]})
    return points

  def handle_json_GET_boundboxstops(self, params):
    """Return a list of up to 'limit' stops within bounding box with 'n','e'
    and 's','w' in the NE and SW corners. Does not handle boxes crossing
    longitude line 180."""
    schedule = self.server.schedule
    n = float(params.get('n'))
    e = float(params.get('e'))
    s = float(params.get('s'))
    w = float(params.get('w'))
    limit = int(params.get('limit'))

    stops = Stop.GetStopsInBoundingBox(schedule._db, n, e, s, w, limit)
    stopsinbounds = []
    for stop in stops:
      stopsinbounds.append({
           'id': stop[0],
           'name': stop[1],
           'lat': stop[2],
           'lng': stop[3],
           'parentStation': stop[4],
           'locationType': stop[5]
           })
    return stopsinbounds

      

def main(RequestHandlerClass = ScheduleEditorRequestHandler):
  usage = (
'''%prog [options] [<input GTFS.zip>]

Runs a webserver that lets you explore a <input GTFS.zip> in your browser.

If <input GTFS.zip> is omited the filename is read from the console.
Dragging a file into the console may enter the filename.
''')
  parser = util.OptionParserLongError(
      usage=usage, version='%prog ' + transitfeed.__version__)
  parser.add_option('--feed_filename', '--feed',
                    dest='feed_filename',
                    help='file name of feed to load')
  parser.add_option('--key', dest='key',
                    help='Google Maps API key or the name '
                    'of a text file that contains an API key')
  parser.add_option('--host', dest='host',
                    help='Host name of Google Maps')
  parser.add_option('--port', dest='port', type='int',
                    help='port on which to listen')
  parser.add_option('--file_dir', dest='file_dir',
                    help='directory containing static files')
  parser.add_option('-n', '--noprompt', action='store_false',
                    dest='manual_entry',
                    help='disable interactive prompts')
  parser.set_defaults(port=8765,
                      host='maps.google.com',
                      file_dir=FindDefaultFileDir(),
                      manual_entry=True)
  (options, args) = parser.parse_args()

  if not os.path.isfile(
      os.path.join(options.file_dir, 'index.html')):
    print ("Can't find index.html with --file_dir=%s" %
      options.file_dir)
    exit(1)

  if not options.feed_filename and len(args) == 1:
    options.feed_filename = args[0]

  if not options.feed_filename and options.manual_entry:
    options.feed_filename = raw_input(
                              'Enter Feed Location: ').strip('"')

  default_key_file = GetDefaultKeyFilePath()
  if not options.key and os.path.isfile(default_key_file):
    options.key = open(default_key_file).read().strip()

  if options.key and os.path.isfile(options.key):
    options.key = open(options.key).read().strip()

  schedule = EditableSchedule(
               options.feed_filename,
               problem_reporter=transitfeed.ProblemReporter())
  print 'Loading data from feed "%s"...' % options.feed_filename
  print '(this may take a few minutes for larger cities)'
  schedule.Load(options.feed_filename)

  server = StoppableHTTPServer(server_address=('', options.port),
                          RequestHandlerClass=RequestHandlerClass)
  server.key = options.key
  server.schedule = schedule
  server.file_dir = options.file_dir
  server.host = options.host
  server.feed_path = options.feed_filename

  print ("To view, point your browser at http://localhost:%d/" %
         (server.server_port))
  server.serve_forever()

def FindPy2ExeBase():
  """If this is running in py2exe return the install directory else return
  None"""
  # py2exe puts gtfsscheduleviewer in library.zip. For py2exe setup.py is
  # configured to put the data next to library.zip.
  windows_ending = gtfsscheduleeditor.__file__.find('\\library.zip\\')
  if windows_ending != -1:
    return transitfeed.__file__[:windows_ending]
  else:
    return None

def FindDefaultFileDir():
  """Return the path of the directory containing the static files. By default
  the directory is called 'files'. The location depends on where setup.py put
  it.
  FindDefaultFileDir is invoked in main function where the parser sets default
  values. It is modified here to set the gtfsscheduleeditor directory as the
  default directory.
  """
  base = FindPy2ExeBase()
  if base:
    return os.path.join(base, 'schedule_editor_files')
  else:
    # For all other distributions 'files' is in the gtfsscheduleviewer
    # directory.
    base = os.path.dirname(gtfsscheduleeditor.__file__)  # Strip __init__.py
    return os.path.join(base, 'files')

if __name__ == '__main__':
  print "starting editor ..."
  main()
